package ged.diagram.edit.parts;

import java.util.Collections;
import java.util.List;

import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.Label;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.transaction.RunnableWithResult;
import org.eclipse.gef.AccessibleEditPart;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.Request;
import org.eclipse.gef.requests.DirectEditRequest;
import org.eclipse.gef.tools.DirectEditManager;
import org.eclipse.gmf.runtime.common.ui.services.parser.IParser;
import org.eclipse.gmf.runtime.common.ui.services.parser.IParserEditStatus;
import org.eclipse.gmf.runtime.common.ui.services.parser.ParserEditStatus;
import org.eclipse.gmf.runtime.common.ui.services.parser.ParserOptions;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IBorderItemEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.ITextAwareEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.LabelEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editpolicies.LabelDirectEditPolicy;
import org.eclipse.gmf.runtime.diagram.ui.figures.IBorderItemLocator;
import org.eclipse.gmf.runtime.diagram.ui.l10n.DiagramColorRegistry;
import org.eclipse.gmf.runtime.diagram.ui.requests.RequestConstants;
import org.eclipse.gmf.runtime.diagram.ui.tools.TextDirectEditManager;
import org.eclipse.gmf.runtime.draw2d.ui.figures.WrappingLabel;
import org.eclipse.gmf.runtime.emf.core.util.EObjectAdapter;
import org.eclipse.gmf.runtime.emf.ui.services.parser.ISemanticParser;
import org.eclipse.gmf.runtime.notation.FontStyle;
import org.eclipse.gmf.runtime.notation.NotationPackage;
import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.viewers.ICellEditorValidator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.accessibility.AccessibleEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Image;

public class NodeExternalLabelEditPart extends LabelEditPart implements ITextAwareEditPart, IBorderItemEditPart {

	public static final int VISUAL_ID = 7001;
	
	private DirectEditManager manager;
	
	private IParser parser;
	
	private List<?> parserElements;
	
	private String defaultText;
	
	static {
		//Subclasses of LabelEditPart should call this to register their labels default position.
		registerSnapBackPosition(
				ged.diagram.part.GedVisualIDRegistry
						.getType(ged.diagram.edit.parts.NodeExternalLabelEditPart.VISUAL_ID),
				new Point(0, 0));
	}
	
	public NodeExternalLabelEditPart(View view) {
		super(view);
	}

	protected void createDefaultEditPolicies() {
		super.createDefaultEditPolicies();
		installEditPolicy(EditPolicy.DIRECT_EDIT_ROLE,
				new LabelDirectEditPolicy());
		installEditPolicy(
				EditPolicy.SELECTION_FEEDBACK_ROLE,
				new ged.diagram.edit.policies.GedTextSelectionEditPolicy());
	}
	
	@Override
	public IBorderItemLocator getBorderItemLocator() {
		IFigure parentFigure = getFigure().getParent();
		if (parentFigure != null && parentFigure.getLayoutManager() != null) {
			Object constraint = parentFigure.getLayoutManager().getConstraint(
					getFigure());
			return (IBorderItemLocator) constraint;
		}
		return null;
	}

	@Override
	public void refreshBounds() {
		int x = ((Integer) getStructuralFeatureValue(NotationPackage.eINSTANCE
				.getLocation_X())).intValue();
		int y = ((Integer) getStructuralFeatureValue(NotationPackage.eINSTANCE
				.getLocation_Y())).intValue();
		int width = ((Integer) getStructuralFeatureValue(NotationPackage.eINSTANCE
				.getSize_Width())).intValue();
		int height = ((Integer) getStructuralFeatureValue(NotationPackage.eINSTANCE
				.getSize_Height())).intValue();
		getBorderItemLocator()
				.setConstraint(new Rectangle(x, y, width, height));
	}
	
	protected String getLabelTextHelper(IFigure figure) {
		if (figure instanceof WrappingLabel) {
			return ((WrappingLabel) figure).getText();
		} else {
			return ((Label) figure).getText();
		}
	}
	
	protected void setLabelTextHelper(IFigure figure, String text) {
		if (figure instanceof WrappingLabel) {
			((WrappingLabel) figure).setText(text);
		} else {
			((Label) figure).setText(text);
		}
	}
	
	protected Image getLabelIconHelper(IFigure figure) {
		if (figure instanceof WrappingLabel) {
			return ((WrappingLabel) figure).getIcon();
		} else {
			return ((Label) figure).getIcon();
		}
	}
	
	protected void setLabelIconHelper(IFigure figure, Image icon) {
		if (figure instanceof WrappingLabel) {
			((WrappingLabel) figure).setIcon(icon);
		} else {
			((Label) figure).setIcon(icon);
		}
	}
	
	public void setLabel(IFigure figure) {
		unregisterVisuals();
		setFigure(figure);
		defaultText = getLabelTextHelper(figure);
		registerVisuals();
		refreshVisuals();
	} 
	
	@SuppressWarnings("rawtypes")
	protected List getModelChildren() {
		return Collections.EMPTY_LIST;
	}

	public IGraphicalEditPart getChildBySemanticHint(String semanticHint) {
		return null;
	}
	
	protected EObject getParserElement() {
		return resolveSemanticElement();
	}
	
	protected Image getLabelIcon() {
		EObject parserElement = getParserElement();
		if (parserElement == null) {
			return null;
		}
		return ged.diagram.providers.GedElementTypes
				.getImage(parserElement.eClass());
	}
	
	protected String getLabelText() {
		String text = null;
		EObject parserElement = getParserElement();
		if (parserElement != null && getParser() != null) {
			text = getParser().getPrintString(
					new EObjectAdapter(parserElement),
					getParserOptions().intValue());
		}
		if (text == null || text.length() == 0) {
			text = defaultText;
		}
		return text;
	}
	
	@Override
	public void setLabelText(String text) {
		setLabelTextHelper(getFigure(), text);
		Object pdEditPolicy = getEditPolicy(EditPolicy.PRIMARY_DRAG_ROLE);
		if (pdEditPolicy instanceof ged.diagram.edit.policies.GedTextSelectionEditPolicy) {
			((ged.diagram.edit.policies.GedTextSelectionEditPolicy) pdEditPolicy)
					.refreshFeedback();
		}
		Object sfEditPolicy = getEditPolicy(EditPolicy.SELECTION_FEEDBACK_ROLE);
		if (sfEditPolicy instanceof ged.diagram.edit.policies.GedTextSelectionEditPolicy) {
			((ged.diagram.edit.policies.GedTextSelectionEditPolicy) sfEditPolicy)
					.refreshFeedback();
		}
	}
	
	@Override
	public String getEditText() {
		if (getParserElement() == null || getParser() == null) {
			return ""; //$NON-NLS-1$
		}
		return getParser().getEditString(
				new EObjectAdapter(getParserElement()),
				getParserOptions().intValue());
	}

	protected boolean isEditable() {
		return getParser() != null;
	}
	
	@Override
	public ICellEditorValidator getEditTextValidator() {
		return new ICellEditorValidator() {

			public String isValid(final Object value) {
				if (value instanceof String) {
					final EObject element = getParserElement();
					final IParser parser = getParser();
					try {
						IParserEditStatus valid = (IParserEditStatus) getEditingDomain()
								.runExclusive(
										new RunnableWithResult.Impl<IParserEditStatus>() {

											public void run() {
												setResult(parser
														.isValidEditString(
																new EObjectAdapter(
																		element),
																(String) value));
											}
										});
						return valid.getCode() == ParserEditStatus.EDITABLE ? null
								: valid.getMessage();
					} catch (InterruptedException ie) {
						ie.printStackTrace();
					}
				}

				// shouldn't get here
				return null;
			}
		};
	}
	
	public IContentAssistProcessor getCompletionProcessor() {
		if (getParserElement() == null || getParser() == null) {
			return null;
		}
		return getParser().getCompletionProcessor(
				new EObjectAdapter(getParserElement()));
	}

	@Override
	public ParserOptions getParserOptions() {
		return ParserOptions.NONE;
	}

	@Override
	public IParser getParser() {
		if (parser == null) {
			parser = ged.diagram.providers.GedParserProvider
					.getParser(
							ged.diagram.providers.GedElementTypes.Node_2001,
							getParserElement(),
							ged.diagram.part.GedVisualIDRegistry
									.getType(ged.diagram.edit.parts.NodeExternalLabelEditPart.VISUAL_ID));
		}
		return parser;
	}
	
	protected DirectEditManager getManager() {
		if (manager == null) {
			setManager(new TextDirectEditManager(this,
					TextDirectEditManager.getTextCellEditorClass(this),
					ged.diagram.edit.parts.GedEditPartFactory
							.getTextCellEditorLocator(this)));
		}
		return manager;
	}
	
	protected void setManager(DirectEditManager manager) {
		this.manager = manager;
	}
	
	protected void performDirectEdit() {
		getManager().show();
	}
	
	protected void performDirectEdit(Point eventLocation) {
		if (getManager().getClass() == TextDirectEditManager.class) {
			((TextDirectEditManager) getManager()).show(eventLocation
					.getSWTPoint());
		}
	}
	
	private void performDirectEdit(char initialCharacter) {
		if (getManager() instanceof TextDirectEditManager) {
			((TextDirectEditManager) getManager()).show(initialCharacter);
		} else {
			performDirectEdit();
		}
	}
		
		protected void performDirectEditRequest (Request request) {
			final Request theRequest = request;
			try {
				getEditingDomain().runExclusive(new Runnable() {

					public void run() {
						if (isActive() && isEditable()) {
							if (theRequest
									.getExtendedData()
									.get(RequestConstants.REQ_DIRECTEDIT_EXTENDEDDATA_INITIAL_CHAR) instanceof Character) {
								Character initialChar = (Character) theRequest
										.getExtendedData()
										.get(RequestConstants.REQ_DIRECTEDIT_EXTENDEDDATA_INITIAL_CHAR);
								performDirectEdit(initialChar.charValue());
							} else if ((theRequest instanceof DirectEditRequest)
									&& (getEditText().equals(getLabelText()))) {
								DirectEditRequest editRequest = (DirectEditRequest) theRequest;
								performDirectEdit(editRequest.getLocation());
							} else {
								performDirectEdit();
							}
						}
					}
				});
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		protected void refreshVisuals() {
			super.refreshVisuals();
			refreshLabel();
			refreshFont();
			refreshFontColor();
			refreshUnderline();
			refreshStrikeThrough();
		}
		
		protected void refreshLabel() {
			setLabelTextHelper(getFigure(), getLabelText());
			setLabelIconHelper(getFigure(), getLabelIcon());
			Object pdEditPolicy = getEditPolicy(EditPolicy.PRIMARY_DRAG_ROLE);
			if (pdEditPolicy instanceof ged.diagram.edit.policies.GedTextSelectionEditPolicy) {
				((ged.diagram.edit.policies.GedTextSelectionEditPolicy) pdEditPolicy)
						.refreshFeedback();
			}
			Object sfEditPolicy = getEditPolicy(EditPolicy.SELECTION_FEEDBACK_ROLE);
			if (sfEditPolicy instanceof ged.diagram.edit.policies.GedTextSelectionEditPolicy) {
				((ged.diagram.edit.policies.GedTextSelectionEditPolicy) sfEditPolicy)
						.refreshFeedback();
			}
		}
		
		protected void refreshUnderline() {
			FontStyle style = (FontStyle) getFontStyleOwnerView().getStyle(
					NotationPackage.eINSTANCE.getFontStyle());
			if (style != null && getFigure() instanceof WrappingLabel) {
				((WrappingLabel) getFigure()).setTextUnderline(style.isUnderline());
			}
		}
		
		protected void refreshStrikeThrough() {
			FontStyle style = (FontStyle) getFontStyleOwnerView().getStyle(
					NotationPackage.eINSTANCE.getFontStyle());
			if (style != null && getFigure() instanceof WrappingLabel) {
				((WrappingLabel) getFigure()).setTextStrikeThrough(style
						.isStrikeThrough());
			}
		}
		
		protected void refreshFont() {
			FontStyle style = (FontStyle) getFontStyleOwnerView().getStyle(
					NotationPackage.eINSTANCE.getFontStyle());
			if (style != null) {
				FontData fontData = new FontData(style.getFontName(),
						style.getFontHeight(), (style.isBold() ? SWT.BOLD
								: SWT.NORMAL)
								| (style.isItalic() ? SWT.ITALIC : SWT.NORMAL));
				setFont(fontData);
			}
		}
		
		protected void setFontColor(Color color) {
			getFigure().setForegroundColor(color);
		}
		
		protected void addSemanticListeners() {
			if (getParser() instanceof ISemanticParser) {
				EObject element = resolveSemanticElement();
				parserElements = ((ISemanticParser) getParser())
						.getSemanticElementsBeingParsed(element);
				for (int i = 0; i < parserElements.size(); i++) {
					addListenerFilter(
							"SemanticModel" + i, this, (EObject) parserElements.get(i)); //$NON-NLS-1$
				}
			} else {
				super.addSemanticListeners();
			}
		}
		
		protected void removeSemanticListeners() {
			if (parserElements != null) {
				for (int i = 0; i < parserElements.size(); i++) {
					removeListenerFilter("SemanticModel" + i); //$NON-NLS-1$
				}
			} else {
				super.removeSemanticListeners();
			}
		}
		
		protected AccessibleEditPart getAccessibleEditPart() {
			if (accessibleEP == null) {
				accessibleEP = new AccessibleGraphicalEditPart() {

					public void getName(AccessibleEvent e) {
						e.result = getLabelTextHelper(getFigure());
					}
				};
			}
			return accessibleEP;
		}
		
		private View getFontStyleOwnerView() {
			return getPrimaryView();
		}
		
		protected void handleNotificationEvent(Notification event) {
			Object feature = event.getFeature();
			if (NotationPackage.eINSTANCE.getFontStyle_FontColor().equals(feature)) {
				Integer c = (Integer) event.getNewValue();
				setFontColor(DiagramColorRegistry.getInstance().getColor(c));
			} else if (NotationPackage.eINSTANCE.getFontStyle_Underline().equals(
					feature)) {
				refreshUnderline();
			} else if (NotationPackage.eINSTANCE.getFontStyle_StrikeThrough()
					.equals(feature)) {
				refreshStrikeThrough();
			} else if (NotationPackage.eINSTANCE.getFontStyle_FontHeight().equals(
					feature)
					|| NotationPackage.eINSTANCE.getFontStyle_FontName().equals(
							feature)
					|| NotationPackage.eINSTANCE.getFontStyle_Bold()
							.equals(feature)
					|| NotationPackage.eINSTANCE.getFontStyle_Italic().equals(
							feature)) {
				refreshFont();
			} else {
				if (getParser() != null
						&& getParser().isAffectingEvent(event,
								getParserOptions().intValue())) {
					refreshLabel();
				}
				if (getParser() instanceof ISemanticParser) {
					ISemanticParser modelParser = (ISemanticParser) getParser();
					if (modelParser.areSemanticElementsAffected(null, event)) {
						removeSemanticListeners();
						if (resolveSemanticElement() != null) {
							addSemanticListeners();
						}
						refreshLabel();
					}
				}
			}
			super.handleNotificationEvent(event);
		}
		
		protected IFigure createFigure() {
			IFigure label = createFigurePrim();
			defaultText = getLabelTextHelper(label);
			return label;
		}
		
		protected IFigure createFigurePrim() {
			return new NodeLabelFigure();
		}
		
		public class NodeLabelFigure extends WrappingLabel {

			/**
			 * @generated
			 */
			public NodeLabelFigure() {
				this.setText("Node");
			}

		}

}
